#[derive(Debug, PartialEq, Eq, Clone, Default, Hash)]
pub enum TokenKind {
  #[default]
  Illegal,
  Eof,

  Identifier,
  Integer,

  Assign,   // =
  Plus,     // +
  Minus,    // -
  Asterisk, // *
  Slash,    // /

  Bang,      // !
  Greater,   // >
  Less,      // <
  Equal,     // ==
  BangEqual, // !=

  Comma,     // ,
  Semicolon, // ;
  Colon,     // :

  LeftParen,    // (
  RightParen,   // )
  LeftBrace,    // {
  RightBrace,   // }
  LeftBracket,  // [
  RightBracket, // ]

  Function,
  Let,
  If,
  Else,
  Return,
  True,
  False,

  String,
}

impl std::fmt::Display for TokenKind {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      Self::Illegal => write!(f, "Illegal"),
      Self::Eof => write!(f, "Eof"),
      Self::Identifier => write!(f, "Identifier"),
      Self::Integer => write!(f, "Integer"),
      Self::Assign => write!(f, "="),
      Self::Plus => write!(f, "+"),
      Self::Minus => write!(f, "-"),
      Self::Asterisk => write!(f, "*"),
      Self::Slash => write!(f, "/"),
      Self::Bang => write!(f, "!"),
      Self::Greater => write!(f, ">"),
      Self::Less => write!(f, "<"),
      Self::Equal => write!(f, "=="),
      Self::BangEqual => write!(f, "!="),
      Self::Comma => write!(f, ","),
      Self::Semicolon => write!(f, ";"),
      Self::LeftParen => write!(f, "("),
      Self::RightParen => write!(f, ")"),
      Self::LeftBrace => write!(f, "{{"),
      Self::RightBrace => write!(f, "}}"),
      Self::LeftBracket => write!(f, "["),
      Self::RightBracket => write!(f, "]"),
      Self::Function => write!(f, "Function"),
      Self::Let => write!(f, "Let"),
      Self::If => write!(f, "if"),
      Self::Else => write!(f, "else"),
      Self::Return => write!(f, "return"),
      Self::True => write!(f, "true"),
      Self::False => write!(f, "false"),
      Self::String => write!(f, "String"),
      Self::Colon => write!(f, ":"),
    }
  }
}

#[derive(Debug, Clone, PartialEq, Default)]
pub struct Token {
  pub kind: TokenKind,
  pub literal: String,
}

impl Token {
  pub fn new(kind: TokenKind, literal: String) -> Self {
    Self { kind, literal }
  }
}

pub fn lookup_identifier(identifier: String) -> TokenKind {
  match identifier.as_str() {
    "fn" => TokenKind::Function,
    "let" => TokenKind::Let,
    "if" => TokenKind::If,
    "else" => TokenKind::Else,
    "return" => TokenKind::Return,
    "true" => TokenKind::True,
    "false" => TokenKind::False,

    _ => TokenKind::Identifier,
  }
}
